﻿using System;
using System.IO;
using System.Text.RegularExpressions;

namespace Volo.Abp.Bundling.Styles;

public static class CssRelativePath
{
    private readonly static Regex RxUrl = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\1\s*\)", RegexOptions.IgnoreCase | RegexOptions.Compiled);

    public static string Adjust(
        string cssFileContents,
        string absoluteInputFilePath,
        string absoluteOutputPath)
    {
        var matches = RxUrl.Matches(cssFileContents);

        if (matches.Count <= 0)
        {
            return cssFileContents;
        }

        var cssDirectoryPath = Path.GetDirectoryName(absoluteInputFilePath)!;

        foreach (Match match in matches)
        {
            var quoteDelimiter = match.Groups[1].Value; //url('') vs url("")
            var relativePathToCss = match.Groups[2].Value;

            // Ignore root relative references
            if (relativePathToCss.StartsWith("/", StringComparison.Ordinal))
            {
                continue;
            }

            //prevent query string from causing error
            var pathAndQuery = relativePathToCss.Split(new[] { '?' }, 2, StringSplitOptions.RemoveEmptyEntries);
            var pathOnly = pathAndQuery[0];
            var queryOnly = pathAndQuery.Length == 2 ? pathAndQuery[1] : string.Empty;

            var absolutePath = GetAbsolutePath(cssDirectoryPath, pathOnly);
            var serverRelativeUrl = MakeRelative(absoluteOutputPath, absolutePath);

            if (!string.IsNullOrEmpty(queryOnly))
            {
                serverRelativeUrl += "?" + queryOnly;
            }

            var replace = string.Format("url({0}{1}{0})", quoteDelimiter, serverRelativeUrl);

            cssFileContents = cssFileContents.Replace(match.Groups[0].Value, replace);
        }

        return cssFileContents;
    }

    private static string GetAbsolutePath(string cssFilePath, string pathOnly)
    {
        return Path.GetFullPath(Path.Combine(cssFilePath, pathOnly));
    }

    private const string Protocol = "file:///";

    private static string MakeRelative(string baseFile, string file)
    {
        if (string.IsNullOrEmpty(file))
        {
            return file;
        }

        var baseUri = new Uri(Protocol + baseFile, UriKind.RelativeOrAbsolute);
        var fileUri = new Uri(Protocol + file, UriKind.RelativeOrAbsolute);

        return baseUri.IsAbsoluteUri ? Uri.UnescapeDataString(baseUri.MakeRelativeUri(fileUri).ToString()) : baseUri.ToString();
    }
}
